package com.ksyun.campus.client;

import com.ksyun.campus.client.domain.*;
import com.ksyun.campus.client.util.HttpClientUtil;
import com.ksyun.campus.client.util.ZkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;

public class EFileSystem extends FileSystem{
    private static final Logger logger = LoggerFactory.getLogger(EFileSystem.class);
    
    // ZooKeeper连接字符串
    private static final String DEFAULT_ZK_CONNECT_STRING = "localhost:2181";
    
    private String metaServerHost;
    private int metaServerPort;
    private ZkUtil zkUtil;
    private String zkConnectString;
    private boolean metaServerInitialized = false;

    public EFileSystem() {
        this("default");
    }

    public EFileSystem(String fileSystemName) {
        this.defaultFileSystemName = fileSystemName;
        this.zkConnectString = DEFAULT_ZK_CONNECT_STRING;
        initZkUtil();
    }

    
    private void initZkUtil() {
        try {
            this.zkUtil = new ZkUtil(zkConnectString);
            this.zkUtil.init();
            logger.info("ZooKeeper初始化成功: {}", zkConnectString);
            
            // 尝试初始化metaServer信息，但不因此失败而清空zkUtil
            try {
                initMetaServerFromZk();
            } catch (Exception metaE) {
                logger.warn("首次获取metaServer信息失败，将在需要时重试: {}", metaE.getMessage());
            }
        } catch (Exception e) {
            logger.error("ZooKeeper连接失败: {}", zkConnectString, e);
            this.zkUtil = null;
        }
    }
    
    /**
     * 从ZooKeeper获取metaServer信息
     */
    private void initMetaServerFromZk() {
        try {
            if (zkUtil != null) {
                ClusterInfo clusterInfo = zkUtil.getClusterInfo();
                if (clusterInfo != null) {
                    MetaServerMsg metaServer = null;
                    String serverType = null;
                    
                    // 优先使用master，如果没有master则使用slave
                    if (clusterInfo.getMasterMetaServer() != null) {
                        metaServer = clusterInfo.getMasterMetaServer();
                        serverType = "master";
                    } else if (clusterInfo.getSlaveMetaServer() != null) {
                        metaServer = clusterInfo.getSlaveMetaServer();
                        serverType = "slave";
                    }
                    
                    if (metaServer != null) {
                        this.metaServerHost = metaServer.getHost();
                        this.metaServerPort = metaServer.getPort();
                        this.metaServerInitialized = true;
                        logger.info("从ZooKeeper获取{}MetaServer信息成功: {}:{}", serverType, metaServerHost, metaServerPort);
                    } else {
                        logger.error("从ZooKeeper获取metaServer信息失败，未找到任何可用的metaServer");
                        throw new RuntimeException("无法从ZooKeeper获取metaServer信息");
                    }
                } else {
                    logger.error("从ZooKeeper获取集群信息失败");
                    throw new RuntimeException("无法从ZooKeeper获取集群信息");
                }
            } else {
                logger.error("ZooKeeper未初始化，无法获取metaServer信息");
                throw new RuntimeException("ZooKeeper未初始化");
            }
        } catch (Exception e) {
            logger.error("从ZooKeeper获取metaServer信息异常", e);
            throw new RuntimeException("获取metaServer信息失败", e);
        }
    }
    
    /**
     * 确保metaServer已初始化
     */
    private void ensureMetaServerInitialized() {
        if (!metaServerInitialized) {
            if (zkUtil == null) {
                logger.error("ZooKeeper未初始化，无法获取metaServer信息");
                throw new RuntimeException("ZooKeeper未初始化，请检查ZooKeeper服务是否正常运行");
            }
            initMetaServerFromZk();
        }
    }


    public FSInputStream open(String path) {
        logger.info("Opening file: {}", path);
        ensureMetaServerInitialized();
        try {
            // 首先获取文件状态信息，检查文件是否存在且为文件类型
            StatInfo statInfo = getFileStats(path);
            if (statInfo == null) {
                throw new RuntimeException("File not found: " + path);
            }
            if (statInfo.getType() != FileType.File) {
                throw new RuntimeException("Path is not a file: " + path);
            }
            
            // 直接创建FSInputStream，让它在构造函数中调用open接口获取块信息
            // 这样避免了重复调用open接口
            return new FSInputStream(statInfo, metaServerHost, metaServerPort, defaultFileSystemName);
        } catch (Exception e) {
            logger.error("Failed to open file: {}", path, e);
            return null;
        }
    }


    public FSOutputStream create(String path) {
        logger.info("Creating file: {}", path);
        ensureMetaServerInitialized();
        try {
            String url = String.format("http://%s:%d/api/v1/meta/createFile", metaServerHost, metaServerPort);
            Map<String, String> headers = new HashMap<>();
            headers.put("fileSystemName", defaultFileSystemName);
            headers.put("Content-Type", "application/json");
            
            // 构造请求体
            String requestBody = String.format("{\"path\":\"%s\",\"replication\":3,\"permissions\":\"644\",\"owner\":\"user\",\"group\":\"group\"}", path);
            
            String response = HttpClientUtil.post(url, headers, requestBody);
            logger.info("Create response: {}", response);
            
            // 验证响应是否成功
            if (response == null || response.trim().isEmpty()) {
                throw new IOException("Empty response from metaServer");
            }
            
            // 尝试解析响应，检查是否包含错误信息
            if (response.contains("\"success\":false") || response.contains("error") || response.contains("failed")) {
                throw new IOException("File creation failed: " + response);
            }
            
            // 只有在确认文件创建成功后才创建FSOutputStream
            logger.info("File created successfully: {}", path);
            return new FSOutputStream(path, metaServerHost, metaServerPort, defaultFileSystemName);
            
        } catch (Exception e) {
            logger.error("Failed to create file: {}", path, e);
            throw new RuntimeException("Failed to create file: " + path, e);
        }
    }

    public boolean mkdir(String path) {
        logger.info("Creating directory: {}", path);
        ensureMetaServerInitialized();
        try {
            String url = String.format("http://%s:%d/api/v1/meta/mkdirs", metaServerHost, metaServerPort);
            Map<String, String> headers = new HashMap<>();
            headers.put("fileSystemName", defaultFileSystemName);
            headers.put("Content-Type", "application/json");
            
            // 构造请求体
            String requestBody = String.format("{\"path\":\"%s\",\"permissions\":\"755\",\"owner\":\"user\",\"group\":\"group\"}", path);
            
            String response = HttpClientUtil.post(url, headers, requestBody);
            logger.info("Mkdir response: {}", response);
            return true;
        } catch (Exception e) {
            logger.error("Failed to create directory: {}", path, e);
            return false;
        }
    }

    public boolean delete(String path) {
        logger.info("Deleting path: {}", path);
        ensureMetaServerInitialized();
        try {
            // 1. 首先获取文件信息，确定是文件还是目录
            StatInfo statInfo = getFileStats(path);
            if (statInfo == null) {
                logger.error("File not found: {}", path);
                return false;
            }
            
            // 2. 如果是文件，先调用dataServer删除文件数据
            if (statInfo.getType() == FileType.File) {
                deleteFileFromDataServers(path);
            } else if (statInfo.getType() == FileType.Directory) {
                // 如果是目录，调用dataServer删除目录
                deleteDirectoryFromDataServers(path);
            }
            
            // 3. 调用metaServer删除元数据
            String url = String.format("http://%s:%d/api/v1/meta/delete", metaServerHost, metaServerPort);
            Map<String, String> headers = new HashMap<>();
            headers.put("fileSystemName", defaultFileSystemName);
            headers.put("Content-Type", "application/json");
            
            // 构造删除请求体
            String requestBody = String.format("{\"path\":\"%s\",\"recursive\":true}", path);
            
            String response = HttpClientUtil.delete(url, headers, requestBody);
            logger.info("Delete metadata response: {}", response);
            
            return true;
        } catch (Exception e) {
            logger.error("Failed to delete path: {}", path, e);
            return false;
        }
    }
    
    /**
     * 从dataServer删除文件数据
     * 
     * @param path 文件路径
     */
    private void deleteFileFromDataServers(String path) {
        try {
            // 获取集群信息，找到所有的dataServer
            ClusterInfo clusterInfo = getClusterInfo();
            if (clusterInfo == null || clusterInfo.getDataServers() == null) {
                logger.warn("No data servers available for file deletion");
                return;
            }
            
            // 遍历所有dataServer，尝试删除文件
            for (DataServerMsg dataServer : clusterInfo.getDataServers()) {
                try {
                    String deleteUrl = String.format("http://%s:%d/api/delete?path=%s", 
                            dataServer.getHost(), dataServer.getPort(), path);
                    
                    Map<String, String> headers = new HashMap<>();
                    headers.put("fileSystemName", defaultFileSystemName);
                    
                    String response = HttpClientUtil.delete(deleteUrl, headers);
                    logger.debug("Delete from dataServer {}:{} response: {}", 
                            dataServer.getHost(), dataServer.getPort(), response);
                    
                } catch (Exception e) {
                    // 单个dataServer删除失败不影响整体流程，只记录日志
                    logger.warn("Failed to delete file from dataServer {}:{} - {}", 
                            dataServer.getHost(), dataServer.getPort(), e.getMessage());
                }
            }
            
        } catch (Exception e) {
            logger.error("Error deleting file from data servers: {}", path, e);
        }
    }
    
    /**
     * 从dataServer删除目录数据
     * 
     * @param path 目录路径
     */
    private void deleteDirectoryFromDataServers(String path) {
        try {
            // 获取集群信息，找到所有的dataServer
            ClusterInfo clusterInfo = getClusterInfo();
            if (clusterInfo == null || clusterInfo.getDataServers() == null) {
                logger.warn("No data servers available for directory deletion");
                return;
            }
            
            // 遍历所有dataServer，尝试删除目录
            for (DataServerMsg dataServer : clusterInfo.getDataServers()) {
                try {
                    String deleteUrl = String.format("http://%s:%d/api/deleteDir?path=%s&recursive=true", 
                            dataServer.getHost(), dataServer.getPort(), path);
                    
                    Map<String, String> headers = new HashMap<>();
                    headers.put("fileSystemName", defaultFileSystemName);
                    
                    String response = HttpClientUtil.delete(deleteUrl, headers);
                    logger.debug("Delete directory from dataServer {}:{} response: {}", 
                            dataServer.getHost(), dataServer.getPort(), response);
                    
                } catch (Exception e) {
                    // 单个dataServer删除失败不影响整体流程，只记录日志
                    logger.warn("Failed to delete directory from dataServer {}:{} - {}", 
                            dataServer.getHost(), dataServer.getPort(), e.getMessage());
                }
            }
            
        } catch (Exception e) {
            logger.error("Error deleting directory from data servers: {}", path, e);
        }
    }

    public StatInfo getFileStats(String path) {
        logger.info("Getting file stats: {}", path);
        ensureMetaServerInitialized();
        try {
            String url = String.format("http://%s:%d/api/v1/meta/getStatus?path=%s", metaServerHost, metaServerPort, path);
            Map<String, String> headers = new HashMap<>();
            headers.put("fileSystemName", defaultFileSystemName);
            
            String response = HttpClientUtil.get(url, headers);
            logger.info("Get stats response: {}", response);
            
            // 解析ApiResponse格式的响应
            ApiResponse<StatInfo> apiResponse = HttpClientUtil.parseJson(response, 
                new TypeReference<ApiResponse<StatInfo>>(){});
            
            if (!apiResponse.isSuccess()) {
                throw new RuntimeException("Failed to get file stats: " + apiResponse.getMessage());
            }
            
            return apiResponse.getData();
        } catch (Exception e) {
            logger.error("Failed to get file stats: {}", path, e);
            return null;
        }
    }

    public List<StatInfo> listFileStats(String path) {
        logger.info("Listing file stats: {}", path);
        ensureMetaServerInitialized();
        try {
            String url = String.format("http://%s:%d/api/v1/meta/listStatus?path=%s", metaServerHost, metaServerPort, path);
            Map<String, String> headers = new HashMap<>();
            headers.put("fileSystemName", defaultFileSystemName);
            
            String response = HttpClientUtil.get(url, headers);
            logger.info("List stats response: {}", response);
            
            // 解析ApiResponse格式的响应
            ApiResponse<Map<String, Object>> apiResponse = HttpClientUtil.parseJson(response, 
                new TypeReference<ApiResponse<Map<String, Object>>>(){});
            
            if (!apiResponse.isSuccess()) {
                throw new RuntimeException("Failed to list file stats: " + apiResponse.getMessage());
            }
            
            // 从响应中提取entries列表
            Map<String, Object> data = apiResponse.getData();
            if (data != null && data.containsKey("entries")) {
                @SuppressWarnings("unchecked")
                List<Map<String, Object>> entriesList = (List<Map<String, Object>>) data.get("entries");
                
                // 将Map转换为StatInfo对象
                List<StatInfo> statInfoList = new java.util.ArrayList<>();
                for (Map<String, Object> entry : entriesList) {
                    StatInfo statInfo = new StatInfo();
                    statInfo.setPath((String) entry.get("path"));
                    statInfo.setSize(((Number) entry.get("size")).longValue());
                    statInfo.setMtime((String) entry.get("mtime"));
                    
                    String typeStr = (String) entry.get("type");
                    if ("FILE".equals(typeStr)) {
                        statInfo.setType(FileType.File);
                    } else if ("DIRECTORY".equals(typeStr)) {
                        statInfo.setType(FileType.Directory);
                    }
                    
                    @SuppressWarnings("unchecked")
                    List<Map<String, Object>> replicaDataList = (List<Map<String, Object>>) entry.get("replicaData");
                    if (replicaDataList != null) {
                        List<ReplicaData> replicaData = new java.util.ArrayList<>();
                        for (Map<String, Object> replica : replicaDataList) {
                            ReplicaData rd = new ReplicaData();
                            rd.dsNode = (String) replica.get("dsNode");
                            replicaData.add(rd);
                        }
                        statInfo.setReplicaData(replicaData);
                    }
                    
                    statInfoList.add(statInfo);
                }
                return statInfoList;
            }
            
            return new java.util.ArrayList<>();
        } catch (Exception e) {
            logger.error("Failed to list file stats: {}", path, e);
            return null;
        }
    }

    public ClusterInfo getClusterInfo() {
        logger.info("Getting cluster info from ZooKeeper");
        try {
            if (zkUtil != null) {
                // 从ZooKeeper获取集群信息
                ClusterInfo clusterInfo = zkUtil.getClusterInfo();
                if (clusterInfo != null) {
                    logger.info("Cluster info from ZooKeeper: {}", clusterInfo);
                    return clusterInfo;
                } else {
                    logger.error("从ZooKeeper获取集群信息失败");
                    throw new RuntimeException("无法从ZooKeeper获取集群信息");
                }
            } else {
                logger.error("ZooKeeper未初始化，无法获取集群信息");
                throw new RuntimeException("ZooKeeper未初始化");
            }
        } catch (Exception e) {
            logger.error("Failed to get cluster info from ZooKeeper", e);
            throw new RuntimeException("获取集群信息失败", e);
        }
    }
    
    /**
     * 从MetaServer获取详细的集群信息
     * 包含Leader、DataServer列表、配置信息等
     */
    public Map<String, Object> getClusterInfoFromServer() {
        logger.info("Getting detailed cluster info from MetaServer");
        ensureMetaServerInitialized();
        try {
            String url = String.format("http://%s:%d/api/v1/cluster/info", metaServerHost, metaServerPort);
            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Type", "application/json");
            
            String response = HttpClientUtil.get(url, headers);
            logger.info("Cluster info response: {}", response);
            
            // 解析ApiResponse格式的响应
            ApiResponse<Map<String, Object>> apiResponse = HttpClientUtil.parseJson(response, 
                new TypeReference<ApiResponse<Map<String, Object>>>(){});
            
            if (!apiResponse.isSuccess()) {
                throw new RuntimeException("Failed to get cluster info from server: " + apiResponse.getMessage());
            }
            
            return apiResponse.getData();
        } catch (Exception e) {
            logger.error("Failed to get cluster info from MetaServer", e);
            throw new RuntimeException("获取集群信息失败", e);
        }
    }
    
    /**
     * 获取副本分布统计信息
     */
    public Map<String, Object> getReplicaStats() {
        logger.info("Getting replica distribution statistics from MetaServer");
        ensureMetaServerInitialized();
        try {
            String url = String.format("http://%s:%d/api/v1/cluster/replicaStats", metaServerHost, metaServerPort);
            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Type", "application/json");
            
            String response = HttpClientUtil.get(url, headers);
            logger.info("Replica stats response: {}", response);
            
            // 解析ApiResponse格式的响应
            ApiResponse<Map<String, Object>> apiResponse = HttpClientUtil.parseJson(response, 
                new TypeReference<ApiResponse<Map<String, Object>>>(){});
            
            if (!apiResponse.isSuccess()) {
                throw new RuntimeException("Failed to get replica stats: " + apiResponse.getMessage());
            }
            
            return apiResponse.getData();
        } catch (Exception e) {
            logger.error("Failed to get replica stats from MetaServer", e);
            throw new RuntimeException("获取副本统计信息失败", e);
        }
    }
    
    /**
     * 监听集群变化
     */
    public void watchClusterChanges(ZkUtil.ClusterChangeListener listener) {
        if (zkUtil != null) {
            zkUtil.watchClusterChanges(listener);
        }
    }
    
    /**
     * 关闭资源
     */
    public void close() {
        if (zkUtil != null) {
            zkUtil.close();
        }
    }




}
